Skip to content

fix(daemon): reset turnCompletedCleanly on same-run retry teardown - #5341

Merged
AmyShang-alt merged 1 commit into
nexu-io:mainfrom
tomsen02:fix/retry-stale-turn-completed-flag
Jul 10, 2026
Merged

fix(daemon): reset turnCompletedCleanly on same-run retry teardown#5341
AmyShang-alt merged 1 commit into
nexu-io:mainfrom
tomsen02:fix/retry-stale-turn-completed-flag

Conversation

@tomsen02

@tomsen02 tomsen02 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #5340

Why

I'm hardening the daemon's run-lifecycle / event-retention path and hit this while auditing the same-run retry teardown. run.turnCompletedCleanly is a run-scoped success override, but the teardown that recycles the run object between attempts doesn't clear it β€” so a clean-but-empty attempt 1 silently vouches for a crashed attempt 2. The pain: a genuinely failed run is reported succeeded (blank result shown as success), and run_retry_finished reports retry_result: success, which also skews retry telemetry.

This is the same flag as #4197 (set too early) and #5159 (set when it shouldn't be), but a distinct root cause β€” the flag being carried forward across a retry. Neither of those touches tearDownAttemptForRetry.

What users will see

A run whose retried attempt crashes with no output now correctly shows as failed instead of a blank "success". No new surface; behavior correction only.

Surface area

  • None β€” internal daemon fix + regression test only.

Bug fix verification

  • Test path that reproduces the bug: apps/daemon/tests/retry-stale-turn-completed-flag.test.ts β€” drives the full run lifecycle over the daemon HTTP API with a fake claude CLI (clean-but-empty turn β†’ hang β†’ watchdog retry β†’ crash).
  • Did the test go red on main and green on this branch? yes β€” deterministic 8/8 red on main, 8/8 green with the fix (the watchdog window is set wide enough that attempt 1's turn_end is always processed first, so there's no flaky false-green).
  • Additional live verification (real daemon, production HTTP API, same fake CLI): 6/6 succeeded before the fix, 5/5 failed after β€” the one-line teardown reset is the only variable that flips it.

Validation

  • pnpm guard (78 pass) + pnpm --filter @open-design/daemon exec tsc -p tsconfig.json --noEmit (clean)
  • pnpm --filter @open-design/daemon test for the touched area: retry-stale-turn-completed-flag, run-retry-policy, run-retry-runtime, run-failure-classification, run-lifecycle-tracer, daemon-lifecycle, chat-run-artifact-quiet-period, headless-runs β€” all green (133+ tests)

A same-run retry reuses one run object across attempts. tearDownAttemptForRetry
cleared the per-attempt fields (child, exitCode, signal, error, stdinOpen) before
re-spawning but left run.turnCompletedCleanly untouched. That flag is set by a
clean `turn_end` (applyClaudeStreamJsonRunBookkeeping) and read by the terminal
classifier: when attempt 1 finished a clean-but-empty turn (retryable no-output
timeout) and attempt 2 then crashed (exit 1, no output), the crashed run was
classified 'succeeded' off attempt 1's stale flag β€” and reported retry_result:
success in telemetry.

Clear the flag in teardown alongside the other per-attempt resets, with an
invariant comment so future per-attempt state that feeds terminal classification
is cleared in the same place.

Regression: tests/retry-stale-turn-completed-flag.test.ts drives the full run
lifecycle over the daemon HTTP API with a fake claude CLI (empty turn then hang,
crash on retry). Red on main, green with this fix; deterministic 8/8 both ways.
@lefarcen

lefarcen commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

πŸ§ͺ Queued for QA validation β€” this PR touches a run-lifecycle path that needs a manual QA pass before merge. Nothing needed from you right now; we'll update here once it’s validated. Thanks for the thorough repro + verification notes.

@lefarcen
lefarcen requested a review from nettee July 9, 2026 07:54
@lefarcen lefarcen added size/M PR changes 100-300 lines risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps type/bugfix Bug fix needs-validation Runtime change detected; needs human or /explore agent validation. labels Jul 9, 2026

@nettee nettee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomsen-ai I reviewed the retry teardown change in apps/daemon/src/server.ts and the new end-to-end regression in apps/daemon/tests/retry-stale-turn-completed-flag.test.ts. The reset in tearDownAttemptForRetry() matches the close-classification path that still reads run.turnCompletedCleanly, and the new test exercises the exact stale-flag sequence that was misclassifying a crashed retry as success. I couldn’t rerun the daemon Vitest target in this reviewer worktree because dependencies are not installed here (vitest/node_modules missing), but the code path and test coverage both look coherent. Nice catch and a tight regression harness.

πŸ” Powered by Looper Β· runner=reviewer Β· agent=codex Β· An autonomous AI dev team for your GitHub repos.

@lefarcen
lefarcen requested a review from AmyShang-alt July 9, 2026 08:06
@AmyShang-alt

Copy link
Copy Markdown
Contributor

QA validation passed for head a0f2896ab223b68c9b71c2f77cbad1cdfe4973bf.

Local targeted validation:

  • PASS pnpm --dir apps/daemon exec vitest run -c vitest.config.ts tests/retry-stale-turn-completed-flag.test.ts β€” 1 test passed; covers the stale turnCompletedCleanly same-run retry regression.
  • PASS targeted adjacent daemon suite: retry-stale-turn-completed-flag, run-retry-policy, run-failure-classification, run-lifecycle-tracer, daemon-lifecycle, chat-run-artifact-quiet-period, headless-runs β€” 7 files passed, 130 tests passed.
  • PASS pnpm --filter @open-design/daemon exec tsc -p tsconfig.json --noEmit.

Remote PR checks were refreshed and the relevant CI jobs are green. I also checked tests/run-retry-runtime.test.ts; it currently fails with scanRunEventsForUsageAnalytics is not defined, but the same failure reproduces on latest origin/main, so I am treating that as a pre-existing baseline issue rather than a regression from this PR.

Marking this runtime validation as passed.

@AmyShang-alt AmyShang-alt added validated Runtime change validated (via /explore Pass or manual QA). and removed needs-validation Runtime change detected; needs human or /explore agent validation. labels Jul 10, 2026

@AmyShang-alt AmyShang-alt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA validation passed on head a0f2896; approving.

@AmyShang-alt
AmyShang-alt added this pull request to the merge queue Jul 10, 2026
Merged via the queue into nexu-io:main with commit 91179e6 Jul 10, 2026
31 checks passed
@open-design-bot

Copy link
Copy Markdown
Contributor

πŸŽ‰ πŸ“‘ You just leveled up to Giotto

Giotto card for @tomsen02

πŸ“‘ ✨ Sending steady signals.

πŸ™Œ Your contributions are sending a clear signal across the network: you care about making Open Design better. Keep transmitting.

πŸ’› Thanks for helping Open Design move forward. Keep building in the open. πŸš€


πŸ“Š Rank #56 among 400+ contributors

πŸ”— Share on X (English) Β· εˆ†δΊ«εˆ° XοΌˆδΈ­ζ–‡οΌ‰

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps size/M PR changes 100-300 lines type/bugfix Bug fix validated Runtime change validated (via /explore Pass or manual QA).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: same-run retry inherits attempt 1's clean-turn flag, classifying a crashed retry as succeeded

4 participants